home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / ubiquity / install.py < prev    next >
Encoding:
Python Source  |  2009-04-07  |  85.6 KB  |  2,229 lines

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Copyright (C) 2005 Javier Carranza and others for Guadalinex
  5. # Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd.
  6. # Copyright (C) 2007 Mario Limonciello
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  21.  
  22. import sys
  23. import os
  24. import platform
  25. import errno
  26. import stat
  27. import re
  28. import textwrap
  29. import shutil
  30. import subprocess
  31. import time
  32. import struct
  33. import socket
  34. import fcntl
  35. import traceback
  36. import syslog
  37. import gzip
  38. import debconf
  39. import warnings
  40. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  41. import apt_pkg
  42. from apt.cache import Cache
  43. from apt.progress import FetchProgress, InstallProgress
  44. from hashlib import md5
  45.  
  46. sys.path.insert(0, '/usr/lib/ubiquity')
  47.  
  48. from ubiquity import misc
  49. from ubiquity import osextras
  50. from ubiquity.components import language_apply, apt_setup, timezone_apply, \
  51.                                 clock_setup, console_setup_apply, \
  52.                                 usersetup_apply, hw_detect, check_kernels, \
  53.                                 migrationassistant_apply
  54.  
  55. def debconf_disconnect():
  56.     """Disconnect from debconf. This is only to be used as a subprocess
  57.     preexec_fn helper."""
  58.     os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  59.     if 'DEBIAN_HAS_FRONTEND' in os.environ:
  60.         del os.environ['DEBIAN_HAS_FRONTEND']
  61.     if 'DEBCONF_USE_CDEBCONF' in os.environ:
  62.         # Probably not a good idea to use this in /target too ...
  63.         del os.environ['DEBCONF_USE_CDEBCONF']
  64.  
  65. class DebconfFetchProgress(FetchProgress):
  66.     """An object that reports apt's fetching progress using debconf."""
  67.  
  68.     def __init__(self, db, title, info_starting, info):
  69.         FetchProgress.__init__(self)
  70.         self.db = db
  71.         self.title = title
  72.         self.info_starting = info_starting
  73.         self.info = info
  74.         self.old_capb = None
  75.         self.eta = 0.0
  76.  
  77.     def start(self):
  78.         self.db.progress('START', 0, 100, self.title)
  79.         if self.info_starting is not None:
  80.             self.db.progress('INFO', self.info_starting)
  81.         self.old_capb = self.db.capb()
  82.         capb_list = self.old_capb.split()
  83.         capb_list.append('progresscancel')
  84.         self.db.capb(' '.join(capb_list))
  85.  
  86.     # TODO cjwatson 2006-02-27: implement updateStatus
  87.  
  88.     def pulse(self):
  89.         FetchProgress.pulse(self)
  90.         try:
  91.             self.db.progress('SET', int(self.percent))
  92.         except debconf.DebconfError:
  93.             return False
  94.         if self.eta != 0.0:
  95.             time_str = "%d:%02d" % divmod(int(self.eta), 60)
  96.             self.db.subst(self.info, 'TIME', time_str)
  97.             try:
  98.                 self.db.progress('INFO', self.info)
  99.             except debconf.DebconfError:
  100.                 return False
  101.         return True
  102.  
  103.     def stop(self):
  104.         if self.old_capb is not None:
  105.             self.db.capb(self.old_capb)
  106.             self.old_capb = None
  107.             self.db.progress('STOP')
  108.  
  109. class DebconfInstallProgress(InstallProgress):
  110.     """An object that reports apt's installation progress using debconf."""
  111.  
  112.     def __init__(self, db, title, info, error=None):
  113.         InstallProgress.__init__(self)
  114.         self.db = db
  115.         self.title = title
  116.         self.info = info
  117.         self.error_template = error
  118.         self.started = False
  119.         # InstallProgress uses a non-blocking status fd; our run()
  120.         # implementation doesn't need that, and in fact we spin unless the
  121.         # fd is blocking.
  122.         flags = fcntl.fcntl(self.statusfd.fileno(), fcntl.F_GETFL)
  123.         fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,
  124.                     flags & ~os.O_NONBLOCK)
  125.  
  126.     def startUpdate(self):
  127.         self.db.progress('START', 0, 100, self.title)
  128.         self.started = True
  129.  
  130.     def error(self, pkg, errormsg):
  131.         if self.error_template is not None:
  132.             self.db.subst(self.error_template, 'PACKAGE', pkg)
  133.             self.db.subst(self.error_template, 'MESSAGE', errormsg)
  134.             self.db.input('critical', self.error_template)
  135.             self.db.go()
  136.  
  137.     def statusChange(self, pkg, percent, status):
  138.         self.percent = percent
  139.         self.status = status
  140.         self.db.progress('SET', int(percent))
  141.         self.db.subst(self.info, 'DESCRIPTION', status)
  142.         self.db.progress('INFO', self.info)
  143.  
  144.     def updateInterface(self):
  145.         # TODO cjwatson 2006-02-28: InstallProgress.updateInterface doesn't
  146.         # give us a handy way to spot when percentages/statuses change and
  147.         # aren't pmerror/pmconffile, so we have to reimplement it here.
  148.         if self.statusfd is None:
  149.             return False
  150.         try:
  151.             while not self.read.endswith("\n"):
  152.                 r = os.read(self.statusfd.fileno(),1)
  153.                 if not r:
  154.                     return False
  155.                 self.read += r
  156.         except OSError, (err,errstr):
  157.             print errstr
  158.         if self.read.endswith("\n"):
  159.             s = self.read
  160.             (status, pkg, percent, status_str) = s.split(":", 3)
  161.             if status == "pmerror":
  162.                 self.error(pkg, status_str)
  163.             elif status == "pmconffile":
  164.                 # we get a string like this:
  165.                 # 'current-conffile' 'new-conffile' useredited distedited
  166.                 match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
  167.                 if match:
  168.                     self.conffile(match.group(1), match.group(2))
  169.             else:
  170.                 self.statusChange(pkg, float(percent), status_str.strip())
  171.             self.read = ""
  172.         return True
  173.  
  174.     def run(self, pm):
  175.         # Create a subprocess to deal with turning apt status messages into
  176.         # debconf protocol messages.
  177.         child_pid = self.fork()
  178.         if child_pid == 0:
  179.             # child
  180.             os.close(self.writefd)
  181.             try:
  182.                 while self.updateInterface():
  183.                     pass
  184.             except (KeyboardInterrupt, SystemExit):
  185.                 pass # we're going to exit anyway
  186.             except:
  187.                 for line in traceback.format_exc().split('\n'):
  188.                     syslog.syslog(syslog.LOG_WARNING, line)
  189.             os._exit(0)
  190.  
  191.         self.statusfd.close()
  192.  
  193.         # Redirect stdin from /dev/null and stdout to stderr to avoid them
  194.         # interfering with our debconf protocol stream.
  195.         saved_stdin = os.dup(0)
  196.         try:
  197.             null = os.open('/dev/null', os.O_RDONLY)
  198.             os.dup2(null, 0)
  199.             os.close(null)
  200.         except OSError:
  201.             pass
  202.         saved_stdout = os.dup(1)
  203.         os.dup2(2, 1)
  204.  
  205.         # Make sure all packages are installed non-interactively. We
  206.         # don't have enough passthrough magic here to deal with any
  207.         # debconf questions they might ask.
  208.         saved_environ_keys = ('DEBIAN_FRONTEND', 'DEBIAN_HAS_FRONTEND',
  209.                               'DEBCONF_USE_CDEBCONF')
  210.         saved_environ = {}
  211.         for key in saved_environ_keys:
  212.             if key in os.environ:
  213.                 saved_environ[key] = os.environ[key]
  214.         os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  215.         if 'DEBIAN_HAS_FRONTEND' in os.environ:
  216.             del os.environ['DEBIAN_HAS_FRONTEND']
  217.         if 'DEBCONF_USE_CDEBCONF' in os.environ:
  218.             # Probably not a good idea to use this in /target too ...
  219.             del os.environ['DEBCONF_USE_CDEBCONF']
  220.  
  221.         res = pm.ResultFailed
  222.         try:
  223.             res = pm.DoInstall(self.writefd)
  224.         finally:
  225.             # Reap the status-to-debconf subprocess.
  226.             os.close(self.writefd)
  227.             while True:
  228.                 try:
  229.                     (pid, status) = os.waitpid(child_pid, 0)
  230.                     if pid != child_pid:
  231.                         break
  232.                     if os.WIFEXITED(status) or os.WIFSIGNALED(status):
  233.                         break
  234.                 except OSError:
  235.                     break
  236.  
  237.             # Put back stdin and stdout.
  238.             os.dup2(saved_stdin, 0)
  239.             os.close(saved_stdin)
  240.             os.dup2(saved_stdout, 1)
  241.             os.close(saved_stdout)
  242.  
  243.             # Put back the environment.
  244.             for key in saved_environ_keys:
  245.                 if key in saved_environ:
  246.                     os.environ[key] = saved_environ[key]
  247.                 elif key in os.environ:
  248.                     del os.environ[key]
  249.  
  250.         return res
  251.  
  252.     def finishUpdate(self):
  253.         if self.started:
  254.             self.db.progress('STOP')
  255.             self.started = False
  256.  
  257. class InstallStepError(Exception):
  258.     """Raised when an install step fails."""
  259.  
  260.     def __init__(self, message):
  261.         Exception.__init__(self, message)
  262.  
  263. class Install:
  264.  
  265.     def __init__(self):
  266.         """Initial attributes."""
  267.  
  268.         if os.path.isdir('/rofs'):
  269.             self.source = '/rofs'
  270.         elif os.path.isdir('/UNIONFS'):
  271.             # Klaus Knopper says this may not actually work very well
  272.             # because it'll copy the WHOLE WORLD (~12GB).
  273.             self.source = '/UNIONFS'
  274.         else:
  275.             self.source = '/var/lib/ubiquity/source'
  276.         self.target = '/target'
  277.         self.kernel_version = platform.release()
  278.         self.db = debconf.Debconf()
  279.  
  280.         self.select_language_packs()
  281.         use_restricted = True
  282.         try:
  283.             if self.db.get('apt-setup/restricted') == 'false':
  284.                 use_restricted = False
  285.         except debconf.DebconfError:
  286.             pass
  287.         if not use_restricted:
  288.             self.restricted_cache = Cache()
  289.         self.blacklist = {}
  290.         if self.db.get('ubiquity/install/generate-blacklist') == 'true':
  291.             self.db.progress('START', 0, 100, 'ubiquity/install/title')
  292.             self.db.progress('INFO', 'ubiquity/install/blacklist')
  293.             self.generate_blacklist()
  294.  
  295.         apt_pkg.InitConfig()
  296.         apt_pkg.Config.Set("Dir", "/target")
  297.         apt_pkg.Config.Set("Dir::State::status", "/target/var/lib/dpkg/status")
  298.         apt_pkg.Config.Set("APT::GPGV::TrustedKeyring",
  299.                            "/target/etc/apt/trusted.gpg")
  300.         apt_pkg.Config.Set("Acquire::gpgv::Options::",
  301.                            "--ignore-time-conflict")
  302.         apt_pkg.Config.Set("DPkg::Options::", "--root=/target")
  303.         # We don't want apt-listchanges or dpkg-preconfigure, so just clear
  304.         # out the list of pre-installation hooks.
  305.         apt_pkg.Config.Clear("DPkg::Pre-Install-Pkgs")
  306.         apt_pkg.InitSystem()
  307.  
  308.     def excepthook(self, exctype, excvalue, exctb):
  309.         """Crash handler. Dump the traceback to a file so that it can be
  310.         read by the caller."""
  311.  
  312.         if (issubclass(exctype, KeyboardInterrupt) or
  313.             issubclass(exctype, SystemExit)):
  314.             return
  315.  
  316.         tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
  317.         syslog.syslog(syslog.LOG_ERR, "Exception during installation:")
  318.         for line in tbtext.split('\n'):
  319.             syslog.syslog(syslog.LOG_ERR, line)
  320.         tbfile = open('/var/lib/ubiquity/install.trace', 'w')
  321.         print >>tbfile, tbtext
  322.         tbfile.close()
  323.  
  324.         sys.exit(1)
  325.  
  326.     def run(self):
  327.         """Run the install stage: copy everything to the target system, then
  328.         configure it as necessary."""
  329.  
  330.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  331.         self.db.progress('INFO', 'ubiquity/install/mounting_source')
  332.  
  333.         try:
  334.             if self.source == '/var/lib/ubiquity/source':
  335.                 self.mount_source()
  336.  
  337.             self.db.progress('SET', 1)
  338.             self.db.progress('REGION', 1, 75)
  339.             try:
  340.                 self.copy_all()
  341.             except EnvironmentError, e:
  342.                 if e.errno in (errno.ENOENT, errno.EIO, errno.EFAULT,
  343.                                errno.ENOTDIR, errno.EROFS):
  344.                     if e.filename is None:
  345.                         error_template = 'cd_hd_fault'
  346.                     elif e.filename.startswith('/target'):
  347.                         error_template = 'hd_fault'
  348.                     else:
  349.                         error_template = 'cd_fault'
  350.                     error_template = ('ubiquity/install/copying_error/%s' %
  351.                                       error_template)
  352.                     self.db.subst(error_template, 'ERROR', str(e))
  353.                     self.db.input('critical', error_template)
  354.                     self.db.go()
  355.                     # Exit code 3 signals to the frontend that we have
  356.                     # handled this error.
  357.                     sys.exit(3)
  358.                 elif e.errno == errno.ENOSPC:
  359.                     error_template = 'ubiquity/install/copying_error/no_space'
  360.                     self.db.subst(error_template, 'ERROR', str(e))
  361.                     self.db.input('critical', error_template)
  362.                     self.db.go()
  363.                     sys.exit(3)
  364.                 else:
  365.                     raise
  366.  
  367.             self.db.progress('SET', 75)
  368.             self.db.progress('REGION', 75, 76)
  369.             self.db.progress('INFO', 'ubiquity/install/locales')
  370.             self.configure_locales()
  371.  
  372.             self.db.progress('SET', 76)
  373.             self.db.progress('REGION', 76, 77)
  374.             self.db.progress('INFO', 'ubiquity/install/user')
  375.             self.configure_user()
  376.  
  377.             self.db.progress('SET', 77)
  378.             self.db.progress('REGION', 77, 78)
  379.             self.run_target_config_hooks()
  380.  
  381.             self.db.progress('SET', 78)
  382.             self.db.progress('REGION', 78, 79)
  383.             self.db.progress('INFO', 'ubiquity/install/network')
  384.             self.configure_network()
  385.  
  386.             self.db.progress('SET', 79)
  387.             self.db.progress('REGION', 79, 80)
  388.             self.db.progress('INFO', 'ubiquity/install/apt')
  389.             self.configure_apt()
  390.  
  391.             self.db.progress('SET', 80)
  392.             self.db.progress('REGION', 80, 85)
  393.             # Ignore failures from language pack installation.
  394.             try:
  395.                 self.install_language_packs()
  396.             except InstallStepError:
  397.                 pass
  398.             except IOError:
  399.                 pass
  400.             except SystemError:
  401.                 pass
  402.  
  403.             self.db.progress('SET', 85)
  404.             self.db.progress('REGION', 85, 86)
  405.             self.db.progress('INFO', 'ubiquity/install/timezone')
  406.             self.configure_timezone()
  407.  
  408.             self.db.progress('SET', 86)
  409.             self.db.progress('REGION', 86, 87)
  410.             self.db.progress('INFO', 'ubiquity/install/keyboard')
  411.             self.configure_keyboard()
  412.  
  413.             self.db.progress('SET', 87)
  414.             self.db.progress('REGION', 87, 88)
  415.             self.db.progress('INFO', 'ubiquity/install/migrationassistant')
  416.             self.configure_ma()
  417.  
  418.             self.db.progress('SET', 88)
  419.             self.db.progress('REGION', 88, 89)
  420.             self.remove_unusable_kernels()
  421.  
  422.             self.db.progress('SET', 89)
  423.             self.db.progress('REGION', 89, 93)
  424.             self.db.progress('INFO', 'ubiquity/install/hardware')
  425.             self.configure_hardware()
  426.  
  427.             # Tell apt-install to install packages directly from now on.
  428.             apt_install_direct = open('/var/lib/ubiquity/apt-install-direct',
  429.                                       'w')
  430.             apt_install_direct.close()
  431.  
  432.             self.db.progress('SET', 93)
  433.             self.db.progress('REGION', 93, 94)
  434.             self.db.progress('INFO', 'ubiquity/install/bootloader')
  435.             self.configure_bootloader()
  436.  
  437.             self.db.progress('SET', 94)
  438.             self.db.progress('REGION', 94, 95)
  439.             self.db.progress('INFO', 'ubiquity/install/installing')
  440.             self.install_extras()
  441.  
  442.             self.db.progress('SET', 95)
  443.             self.db.progress('REGION', 95, 99)
  444.             self.db.progress('INFO', 'ubiquity/install/removing')
  445.             self.remove_extras()
  446.  
  447.             self.remove_broken_cdrom()
  448.  
  449.             self.copy_dcd()
  450.             self.db.progress('SET', 99)
  451.             self.db.progress('INFO', 'ubiquity/install/log_files')
  452.             self.copy_logs()
  453.  
  454.             self.db.progress('SET', 100)
  455.         finally:
  456.             self.cleanup()
  457.             try:
  458.                 self.db.progress('STOP')
  459.             except (KeyboardInterrupt, SystemExit):
  460.                 raise
  461.             except:
  462.                 pass
  463.  
  464.  
  465.     def copy_file(self, sourcepath, targetpath, md5_check):
  466.         sourcefh = None
  467.         targetfh = None
  468.         try:
  469.             while 1:
  470.                 sourcefh = open(sourcepath, 'rb')
  471.                 targetfh = open(targetpath, 'wb')
  472.                 if md5_check:
  473.                     sourcehash = md5()
  474.                 while 1:
  475.                     buf = sourcefh.read(16 * 1024)
  476.                     if not buf:
  477.                         break
  478.                     targetfh.write(buf)
  479.                     if md5_check:
  480.                         sourcehash.update(buf)
  481.  
  482.                 if not md5_check:
  483.                     break
  484.                 targetfh.close()
  485.                 targetfh = open(targetpath, 'rb')
  486.                 if md5_check:
  487.                     targethash = md5()
  488.                 while 1:
  489.                     buf = targetfh.read(16 * 1024)
  490.                     if not buf:
  491.                         break
  492.                     targethash.update(buf)
  493.                 if targethash.digest() != sourcehash.digest():
  494.                     if targetfh:
  495.                         targetfh.close()
  496.                     if sourcefh:
  497.                         sourcefh.close()
  498.                     error_template = 'ubiquity/install/copying_error/md5'
  499.                     self.db.subst(error_template, 'FILE', targetpath)
  500.                     self.db.input('critical', error_template)
  501.                     self.db.go()
  502.                     response = self.db.get(error_template)
  503.                     if response == 'skip':
  504.                         break
  505.                     elif response == 'abort':
  506.                         syslog.syslog(syslog.LOG_ERR,
  507.                             'MD5 failure on %s' % targetpath)
  508.                         sys.exit(3)
  509.                     elif response == 'retry':
  510.                         pass
  511.                 else:
  512.                     break
  513.         finally:
  514.             if targetfh:
  515.                 targetfh.close()
  516.             if sourcefh:
  517.                 sourcefh.close()
  518.  
  519.     def find_cd_kernel(self):
  520.         """Find the boot kernel on the CD, if possible."""
  521.  
  522.         release_bits = os.uname()[2].split('-')
  523.         if len(release_bits) >= 3:
  524.             subarch = release_bits[2]
  525.         else:
  526.             subarch = None
  527.  
  528.         for prefix in ('vmlinux', 'vmlinuz'):
  529.             kernel = os.path.join('/cdrom/casper', prefix)
  530.             if os.path.exists(kernel):
  531.                 return kernel
  532.  
  533.             if subarch:
  534.                 kernel = os.path.join('/cdrom/casper', subarch, prefix)
  535.                 if os.path.exists(kernel):
  536.                     return kernel
  537.  
  538.                 kernel = os.path.join('/cdrom/casper',
  539.                                       '%s-%s' % (prefix, subarch))
  540.                 if os.path.exists(kernel):
  541.                     return kernel
  542.  
  543.         return None
  544.  
  545.  
  546.     def archdetect(self):
  547.         archdetect = subprocess.Popen(['archdetect'], stdout=subprocess.PIPE)
  548.         answer = archdetect.communicate()[0].strip()
  549.         try:
  550.             return answer.split('/', 1)
  551.         except ValueError:
  552.             return answer, ''
  553.  
  554.  
  555.     def generate_blacklist(self):
  556.         if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
  557.             os.path.exists("/cdrom/casper/filesystem.manifest")):
  558.             desktop_packages = set()
  559.             manifest = open("/cdrom/casper/filesystem.manifest-desktop")
  560.             for line in manifest:
  561.                 if line.strip() != '' and not line.startswith('#'):
  562.                     desktop_packages.add(line.split()[0])
  563.             manifest.close()
  564.             live_packages = set()
  565.             manifest = open("/cdrom/casper/filesystem.manifest")
  566.             for line in manifest:
  567.                 if line.strip() != '' and not line.startswith('#'):
  568.                     live_packages.add(line.split()[0])
  569.             manifest.close()
  570.             difference = live_packages - desktop_packages
  571.         else:
  572.             difference = set()
  573.  
  574.         cache = Cache()
  575.  
  576.         use_restricted = True
  577.         try:
  578.             if self.db.get('apt-setup/restricted') == 'false':
  579.                 use_restricted = False
  580.         except debconf.DebconfError:
  581.             pass
  582.         if not use_restricted:
  583.             for pkg in cache.keys():
  584.                 if (cache[pkg].isInstalled and
  585.                     cache[pkg].section.startswith('restricted/')):
  586.                     difference.add(pkg)
  587.  
  588.         # Keep packages we explicitly installed.
  589.         keep = self.query_recorded_installed()
  590.         arch, subarch = self.archdetect()
  591.  
  592.         # Less than ideal.  Since we cannot know which bootloader we'll need
  593.         # at file copy time, we should figure out why grub still fails when
  594.         # apt-install-direct is present during configure_bootloader (code
  595.         # removed).
  596.         if arch in ('amd64', 'i386', 'lpia'):
  597.             keep.add('grub')
  598.             keep.add('grub-pc')
  599.         elif (arch == 'armel' and
  600.               subarch in ('imx51', 'iop32x', 'ixp4xx', 'orion5x')):
  601.             keep.add('flash-kernel')
  602.         elif arch == 'powerpc' and subarch != 'ps3':
  603.             keep.add('yaboot')
  604.             keep.add('hfsutils')
  605.  
  606.         difference -= self.expand_dependencies_simple(cache, keep, difference)
  607.  
  608.         if len(difference) == 0:
  609.             del cache
  610.             self.blacklist = {}
  611.             return
  612.  
  613.         confirmed_remove = set()
  614.         for pkg in sorted(difference):
  615.             if pkg in confirmed_remove:
  616.                 continue
  617.             would_remove = self.get_remove_list(cache, [pkg], recursive=True)
  618.             if would_remove <= difference:
  619.                 confirmed_remove |= would_remove
  620.                 # Leave these marked for removal in the apt cache to speed
  621.                 # up further calculations.
  622.             else:
  623.                 for removedpkg in would_remove:
  624.                     cachedpkg = self.get_cache_pkg(cache, removedpkg)
  625.                     cachedpkg.markKeep()
  626.         difference = confirmed_remove
  627.         difference = set(filter(
  628.             lambda x: not os.path.exists('/var/lib/dpkg/info/%s.prerm' % x),
  629.             difference))
  630.         cmd = ['dpkg', '-L']
  631.         cmd.extend(difference)
  632.         subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  633.         res = subp.communicate()[0].splitlines()
  634.         u = {}
  635.         for x in res:
  636.             u[x] = 1
  637.         self.blacklist = u
  638.  
  639.     def copy_all(self):
  640.         """Core copy process. This is the most important step of this
  641.         stage. It clones live filesystem into a local partition in the
  642.         selected hard disk."""
  643.  
  644.         files = []
  645.         total_size = 0
  646.  
  647.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  648.         self.db.progress('INFO', 'ubiquity/install/scanning')
  649.  
  650.         # Obviously doing os.walk() twice is inefficient, but I'd rather not
  651.         # suck the list into ubiquity's memory, and I'm guessing that the
  652.         # kernel's dentry cache will avoid most of the slowness anyway.
  653.         walklen = 0
  654.         for entry in os.walk(self.source):
  655.             walklen += 1
  656.         walkpos = 0
  657.         walkprogress = 0
  658.  
  659.         for dirpath, dirnames, filenames in os.walk(self.source):
  660.             walkpos += 1
  661.             if int(float(walkpos) / walklen * 10) != walkprogress:
  662.                 walkprogress = int(float(walkpos) / walklen * 10)
  663.                 self.db.progress('SET', walkprogress)
  664.  
  665.             sourcepath = dirpath[len(self.source) + 1:]
  666.  
  667.             for name in dirnames + filenames:
  668.                 relpath = os.path.join(sourcepath, name)
  669.                 fqpath = os.path.join(dirpath, name)
  670.                 # /etc/fstab was legitimately created by partman, and
  671.                 # shouldn't be copied again.
  672.                 if relpath != "etc/fstab":
  673.                     total_size += os.lstat(fqpath).st_size
  674.                     files.append(relpath)
  675.  
  676.         self.db.progress('SET', 10)
  677.         self.db.progress('INFO', 'ubiquity/install/copying')
  678.  
  679.         # Progress bar handling:
  680.         # We sample progress every half-second (assuming time.time() gives
  681.         # us sufficiently good granularity) and use the average of progress
  682.         # over the last minute or so to decide how much time remains. We
  683.         # don't bother displaying any progress for the first ten seconds in
  684.         # order to allow things to settle down, and we only update the "time
  685.         # remaining" indicator at most every two seconds after that.
  686.  
  687.         copy_progress = 0
  688.         copied_size, counter = 0, 0
  689.         directory_times = []
  690.         time_start = time.time()
  691.         times = [(time_start, copied_size)]
  692.         long_enough = False
  693.         time_last_update = time_start
  694.         if self.db.get('ubiquity/install/md5_check') == 'false':
  695.             md5_check = False
  696.         else:
  697.             md5_check = True
  698.         
  699.         old_umask = os.umask(0)
  700.         for path in files:
  701.             sourcepath = os.path.join(self.source, path)
  702.             targetpath = os.path.join(self.target, path)
  703.             st = os.lstat(sourcepath)
  704.             mode = stat.S_IMODE(st.st_mode)
  705.             if stat.S_ISLNK(st.st_mode):
  706.                 if os.path.lexists(targetpath):
  707.                     os.unlink(targetpath)
  708.                 linkto = os.readlink(sourcepath)
  709.                 os.symlink(linkto, targetpath)
  710.             elif stat.S_ISDIR(st.st_mode):
  711.                 if not os.path.isdir(targetpath):
  712.                     os.mkdir(targetpath, mode)
  713.             elif stat.S_ISCHR(st.st_mode):
  714.                 os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
  715.             elif stat.S_ISBLK(st.st_mode):
  716.                 os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
  717.             elif stat.S_ISFIFO(st.st_mode):
  718.                 os.mknod(targetpath, stat.S_IFIFO | mode)
  719.             elif stat.S_ISSOCK(st.st_mode):
  720.                 os.mknod(targetpath, stat.S_IFSOCK | mode)
  721.             elif stat.S_ISREG(st.st_mode):
  722.                 if '/%s' % path in self.blacklist:
  723.                     syslog.syslog('Not copying %s' % path)
  724.                     continue
  725.                 if os.path.exists(targetpath):
  726.                     os.unlink(targetpath)
  727.                 self.copy_file(sourcepath, targetpath, md5_check)
  728.  
  729.             copied_size += st.st_size
  730.             os.lchown(targetpath, st.st_uid, st.st_gid)
  731.             if not stat.S_ISLNK(st.st_mode):
  732.                 os.chmod(targetpath, mode)
  733.             if stat.S_ISDIR(st.st_mode):
  734.                 directory_times.append((targetpath, st.st_atime, st.st_mtime))
  735.             # os.utime() sets timestamp of target, not link
  736.             elif not stat.S_ISLNK(st.st_mode):
  737.                 os.utime(targetpath, (st.st_atime, st.st_mtime))
  738.  
  739.             if int((copied_size * 90) / total_size) != copy_progress:
  740.                 copy_progress = int((copied_size * 90) / total_size)
  741.                 self.db.progress('SET', 10 + copy_progress)
  742.  
  743.             time_now = time.time()
  744.             if (time_now - times[-1][0]) >= 0.5:
  745.                 times.append((time_now, copied_size))
  746.                 if not long_enough and time_now - times[0][0] >= 10:
  747.                     long_enough = True
  748.                 if long_enough and time_now - time_last_update >= 2:
  749.                     time_last_update = time_now
  750.                     while (time_now - times[0][0] > 60 and
  751.                            time_now - times[1][0] >= 60):
  752.                         times.pop(0)
  753.                     speed = ((times[-1][1] - times[0][1]) /
  754.                              (times[-1][0] - times[0][0]))
  755.                     if speed != 0:
  756.                         time_remaining = int((total_size - copied_size) / speed)
  757.                         if time_remaining < 60:
  758.                             self.db.progress(
  759.                                 'INFO', 'ubiquity/install/copying_minute')
  760.  
  761.         # Apply timestamps to all directories now that the items within them
  762.         # have been copied.
  763.         for dirtime in directory_times:
  764.             (directory, atime, mtime) = dirtime
  765.             try:
  766.                 os.utime(directory, (atime, mtime))
  767.             except OSError:
  768.                 # I have no idea why I've been getting lots of bug reports
  769.                 # about this failing, but I really don't care. Ignore it.
  770.                 pass
  771.  
  772.         # Try some possible locations for the kernel we used to boot. This
  773.         # lets us save a couple of megabytes of CD space.
  774.         bootdir = os.path.join(self.target, 'boot')
  775.         kernel = self.find_cd_kernel()
  776.         if kernel:
  777.             prefix = os.path.basename(kernel).split('-', 1)[0]
  778.             release = os.uname()[2]
  779.             target_kernel = os.path.join(bootdir, '%s-%s' % (prefix, release))
  780.             if os.path.exists(target_kernel):
  781.                 os.unlink(target_kernel)
  782.             self.copy_file(kernel, target_kernel, md5_check)
  783.             os.lchown(target_kernel, 0, 0)
  784.             os.chmod(target_kernel, 0644)
  785.             st = os.lstat(kernel)
  786.             os.utime(target_kernel, (st.st_atime, st.st_mtime))
  787.  
  788.         os.umask(old_umask)
  789.  
  790.         self.db.progress('SET', 100)
  791.         self.db.progress('STOP')
  792.  
  793.  
  794.     def copy_dcd(self):
  795.         """Copy the Distribution Channel Descriptor (DCD) file into the
  796.         installed system."""
  797.  
  798.         dcd = '/cdrom/.disk/ubuntu_dist_channel'
  799.         if os.path.exists(dcd):
  800.             shutil.copy(dcd,
  801.                 os.path.join(self.target, 'var/lib/ubuntu_dist_channel'))
  802.  
  803.     def copy_logs(self):
  804.         """copy log files into installed system."""
  805.  
  806.         target_dir = os.path.join(self.target, 'var/log/installer')
  807.         if not os.path.exists(target_dir):
  808.             os.makedirs(target_dir)
  809.  
  810.         for log_file in ('/var/log/syslog', '/var/log/partman',
  811.                          '/var/log/installer/version', '/var/log/casper.log'):
  812.             target_log_file = os.path.join(target_dir,
  813.                                            os.path.basename(log_file))
  814.             if os.path.isfile(log_file):
  815.                 if not misc.execute('cp', '-a', log_file, target_log_file):
  816.                     syslog.syslog(syslog.LOG_ERR,
  817.                                   'Failed to copy installation log file')
  818.                 os.chmod(target_log_file, stat.S_IRUSR | stat.S_IWUSR)
  819.         try:
  820.             status = open(os.path.join(self.target, 'var/lib/dpkg/status'))
  821.             status_gz = gzip.open(os.path.join(target_dir,
  822.                                                'initial-status.gz'), 'w')
  823.             while True:
  824.                 data = status.read(65536)
  825.                 if not data:
  826.                     break
  827.                 status_gz.write(data)
  828.             status_gz.close()
  829.             status.close()
  830.         except IOError:
  831.             pass
  832.         try:
  833.             if self.db.get('oem-config/enable') == 'true':
  834.                 oem_id = self.db.get('oem-config/id')
  835.                 oem_id_file = open(
  836.                     os.path.join(self.target, 'var/log/installer/oem-id'), 'w')
  837.                 print >>oem_id_file, oem_id
  838.                 oem_id_file.close()
  839.         except (debconf.DebconfError, IOError):
  840.             pass
  841.  
  842.  
  843.     def mount_one_image(self, fsfile, mountpoint=None):
  844.         if os.path.splitext(fsfile)[1] == '.cloop':
  845.             blockdev_prefix = 'cloop'
  846.         elif os.path.splitext(fsfile)[1] == '.squashfs':
  847.             blockdev_prefix = 'loop'
  848.  
  849.         if blockdev_prefix == '':
  850.             raise InstallStepError("No source device found for %s" % fsfile)
  851.  
  852.         dev = ''
  853.         sysloops = filter(lambda x: x.startswith(blockdev_prefix),
  854.                           os.listdir('/sys/block'))
  855.         sysloops.sort()
  856.         for sysloop in sysloops:
  857.             try:
  858.                 sysloopf = open(os.path.join('/sys/block', sysloop, 'size'))
  859.                 sysloopsize = sysloopf.readline().strip()
  860.                 sysloopf.close()
  861.                 if sysloopsize == '0':
  862.                     devnull = open('/dev/null')
  863.                     if osextras.find_on_path('udevadm'):
  864.                         udevinfo_cmd = ['udevadm', 'info']
  865.                     else:
  866.                         udevinfo_cmd = ['udevinfo']
  867.                     udevinfo_cmd.extend(
  868.                         ['-q', 'name', '-p', os.path.join('/block', sysloop)])
  869.                     udevinfo = subprocess.Popen(
  870.                         udevinfo_cmd, stdout=subprocess.PIPE, stderr=devnull)
  871.                     devbase = udevinfo.communicate()[0]
  872.                     devnull.close()
  873.                     if udevinfo.returncode != 0:
  874.                         devbase = sysloop
  875.                     dev = '/dev/%s' % devbase
  876.                     break
  877.             except:
  878.                 continue
  879.  
  880.         if dev == '':
  881.             raise InstallStepError("No loop device available for %s" % fsfile)
  882.  
  883.         misc.execute('losetup', dev, fsfile)
  884.         if mountpoint is None:
  885.             mountpoint = '/var/lib/ubiquity/%s' % sysloop
  886.         if not os.path.isdir(mountpoint):
  887.             os.mkdir(mountpoint)
  888.         if not misc.execute('mount', dev, mountpoint):
  889.             misc.execute('losetup', '-d', dev)
  890.             misc.execute('mount', '-o', 'loop', fsfile, mountpoint)
  891.             dev = 'unused'
  892.  
  893.         return (dev, mountpoint)
  894.  
  895.     def mount_source(self):
  896.         """mounting loop system from cloop or squashfs system."""
  897.  
  898.         self.devs = []
  899.         self.mountpoints = []
  900.  
  901.         if not os.path.isdir(self.source):
  902.             syslog.syslog('mkdir %s' % self.source)
  903.             os.mkdir(self.source)
  904.  
  905.         fs_preseed = self.db.get('ubiquity/install/filesystem-images')
  906.  
  907.         if fs_preseed == '':
  908.             # Simple autodetection on unionfs systems
  909.             mounts = open('/proc/mounts')
  910.             for line in mounts:
  911.                 (device, fstype) = line.split()[1:3]
  912.                 if fstype == 'squashfs' and os.path.exists(device):
  913.                     misc.execute('mount', '--bind', device, self.source)
  914.                     self.mountpoints.append(self.source)
  915.                     mounts.close()
  916.                     return
  917.             mounts.close()
  918.  
  919.             # Manual detection on non-unionfs systems
  920.             fsfiles = ['/cdrom/casper/filesystem.cloop',
  921.                        '/cdrom/casper/filesystem.squashfs',
  922.                        '/cdrom/META/META.squashfs',
  923.                        '/live/image/live/filesystem.squashfs']
  924.  
  925.             for fsfile in fsfiles:
  926.                 if fsfile != '' and os.path.isfile(fsfile):
  927.                     dev, mountpoint = self.mount_one_image(fsfile, self.source)
  928.                     self.devs.append(dev)
  929.                     self.mountpoints.append(mountpoint)
  930.  
  931.         elif len(fs_preseed.split()) == 1:
  932.             # Just one preseeded image.
  933.             if not os.path.isfile(fs_preseed):
  934.                 raise InstallStepError(
  935.                     "Preseeded filesystem image %s not found" % fs_preseed)
  936.  
  937.                 dev, mountpoint = self.mount_one_image(fsfile, self.source)
  938.                 self.devs.append(dev)
  939.                 self.mountpoints.append(mountpoint)
  940.         else:
  941.             # OK, so we need to mount multiple images and unionfs them
  942.             # together.
  943.             for fsfile in fs_preseed.split():
  944.                 if not os.path.isfile(fsfile):
  945.                     raise InstallStepError(
  946.                         "Preseeded filesystem image %s not found" % fsfile)
  947.  
  948.                 dev, mountpoint = self.mount_one_image(fsfile)
  949.                 self.devs.append(dev)
  950.                 self.mountpoints.append(mountpoint)
  951.  
  952.             assert self.devs
  953.             assert self.mountpoints
  954.  
  955.             misc.execute('mount', '-t', 'unionfs', '-o',
  956.                          'dirs=' + ':'.join(map(lambda x: '%s=ro' % x,
  957.                                                 self.mountpoints)),
  958.                          'unionfs', self.source)
  959.             self.mountpoints.append(self.source)
  960.  
  961.     def umount_source(self):
  962.         """umounting loop system from cloop or squashfs system."""
  963.  
  964.         devs = self.devs
  965.         devs.reverse()
  966.         mountpoints = self.mountpoints
  967.         mountpoints.reverse()
  968.  
  969.         for mountpoint in mountpoints:
  970.             if not misc.execute('umount', mountpoint):
  971.                 raise InstallStepError("Failed to unmount %s" % mountpoint)
  972.         for dev in devs:
  973.             if (dev != '' and dev != 'unused' and
  974.                 not misc.execute('losetup', '-d', dev)):
  975.                 raise InstallStepError(
  976.                     "Failed to detach loopback device %s" % dev)
  977.  
  978.  
  979.     def chroot_setup(self, x11=False):
  980.         """Set up /target for safe package management operations."""
  981.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  982.         f = open(policy_rc_d, 'w')
  983.         print >>f, """\
  984. #!/bin/sh
  985. exit 101"""
  986.         f.close()
  987.         os.chmod(policy_rc_d, 0755)
  988.  
  989.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  990.         if os.path.exists(start_stop_daemon):
  991.             os.rename(start_stop_daemon, '%s.REAL' % start_stop_daemon)
  992.         f = open(start_stop_daemon, 'w')
  993.         print >>f, """\
  994. #!/bin/sh
  995. echo 1>&2
  996. echo 'Warning: Fake start-stop-daemon called, doing nothing.' 1>&2
  997. exit 0"""
  998.         f.close()
  999.         os.chmod(start_stop_daemon, 0755)
  1000.  
  1001.         if not os.path.exists(os.path.join(self.target, 'proc/cmdline')):
  1002.             self.chrex('mount', '-t', 'proc', 'proc', '/proc')
  1003.         if not os.path.exists(os.path.join(self.target, 'sys/devices')):
  1004.             self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')
  1005.  
  1006.         if x11 and 'DISPLAY' in os.environ:
  1007.             if 'SUDO_USER' in os.environ:
  1008.                 xauthority = os.path.expanduser('~%s/.Xauthority' %
  1009.                                                 os.environ['SUDO_USER'])
  1010.             else:
  1011.                 xauthority = os.path.expanduser('~/.Xauthority')
  1012.             if os.path.exists(xauthority):
  1013.                 shutil.copy(xauthority,
  1014.                             os.path.join(self.target, 'root/.Xauthority'))
  1015.  
  1016.             if not os.path.isdir(os.path.join(self.target, 'tmp/.X11-unix')):
  1017.                 os.mkdir(os.path.join(self.target, 'tmp/.X11-unix'))
  1018.             misc.execute('mount', '--bind', '/tmp/.X11-unix',
  1019.                          os.path.join(self.target, 'tmp/.X11-unix'))
  1020.  
  1021.     def chroot_cleanup(self, x11=False):
  1022.         """Undo the work done by chroot_setup."""
  1023.         if x11 and 'DISPLAY' in os.environ:
  1024.             misc.execute('umount', os.path.join(self.target, 'tmp/.X11-unix'))
  1025.             try:
  1026.                 os.rmdir(os.path.join(self.target, 'tmp/.X11-unix'))
  1027.             except OSError:
  1028.                 pass
  1029.             try:
  1030.                 os.unlink(os.path.join(self.target, 'root/.Xauthority'))
  1031.             except OSError:
  1032.                 pass
  1033.  
  1034.         self.chrex('umount', '/sys')
  1035.         self.chrex('umount', '/proc')
  1036.  
  1037.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  1038.         os.rename('%s.REAL' % start_stop_daemon, start_stop_daemon)
  1039.  
  1040.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  1041.         os.unlink(policy_rc_d)
  1042.  
  1043.  
  1044.     def run_target_config_hooks(self):
  1045.         """Run hook scripts from /usr/lib/ubiquity/target-config. This allows
  1046.         casper to hook into us and repeat bits of its configuration in the
  1047.         target system."""
  1048.  
  1049.         hookdir = '/usr/lib/ubiquity/target-config'
  1050.  
  1051.         if os.path.isdir(hookdir):
  1052.             # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
  1053.             hooks = filter(lambda entry: '.' not in entry, os.listdir(hookdir))
  1054.             self.db.progress('START', 0, len(hooks), 'ubiquity/install/title')
  1055.             self.db.progress('INFO', 'ubiquity/install/target_hooks')
  1056.             for hookentry in hooks:
  1057.                 hook = os.path.join(hookdir, hookentry)
  1058.                 if not os.access(hook, os.X_OK):
  1059.                     self.db.progress('STEP', 1)
  1060.                     continue
  1061.                 # Errors are ignored at present, although this may change.
  1062.                 subprocess.call(['log-output', '-t', 'ubiquity',
  1063.                                  '--pass-stdout', hook])
  1064.                 self.db.progress('STEP', 1)
  1065.             self.db.progress('STOP')
  1066.  
  1067.  
  1068.     def configure_locales(self):
  1069.         """Apply locale settings to installed system."""
  1070.         dbfilter = language_apply.LanguageApply(None)
  1071.         ret = dbfilter.run_command(auto_process=True)
  1072.         if ret != 0:
  1073.             raise InstallStepError("LanguageApply failed with code %d" % ret)
  1074.  
  1075.         # fontconfig configuration needs to be adjusted based on the
  1076.         # selected locale (from language-selector-common.postinst). Ignore
  1077.         # errors.
  1078.         self.chrex('fontconfig-voodoo', '--auto', '--force', '--quiet')
  1079.  
  1080.  
  1081.     def configure_apt(self):
  1082.         """Configure /etc/apt/sources.list."""
  1083.  
  1084.         # TODO cjwatson 2007-07-06: Much of the following is
  1085.         # cloned-and-hacked from base-installer/debian/postinst. Perhaps we
  1086.         # should come up with a way to avoid this.
  1087.  
  1088.         # Make apt trust CDs. This is not on by default (we think).
  1089.         # This will be left in place on the installed system.
  1090.         apt_conf_tc = open(os.path.join(
  1091.             self.target, 'etc/apt/apt.conf.d/00trustcdrom'), 'w')
  1092.         print >>apt_conf_tc, 'APT::Authentication::TrustCDROM "true";'
  1093.         apt_conf_tc.close()
  1094.  
  1095.         # Avoid clock skew causing gpg verification issues.
  1096.         # This file will be left in place until the end of the install.
  1097.         apt_conf_itc = open(os.path.join(
  1098.             self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'), 'w')
  1099.         print >>apt_conf_itc, \
  1100.             'Acquire::gpgv::Options { "--ignore-time-conflict"; };'
  1101.         apt_conf_itc.close()
  1102.  
  1103.         try:
  1104.             if self.db.get('debian-installer/allow_unauthenticated') == 'true':
  1105.                 apt_conf_au = open(
  1106.                     os.path.join(self.target,
  1107.                                  'etc/apt/apt.conf.d/00AllowUnauthenticated'),
  1108.                     'w')
  1109.                 print >>apt_conf_au, 'APT::Get::AllowUnauthenticated "true";'
  1110.                 print >>apt_conf_au, \
  1111.                     'Aptitude::CmdLine::Ignore-Trust-Violations "true";'
  1112.                 apt_conf_au.close()
  1113.         except debconf.DebconfError:
  1114.             pass
  1115.  
  1116.         # let apt inside the chroot see the cdrom
  1117.         target_cdrom = os.path.join(self.target, 'cdrom')
  1118.         misc.execute('umount', target_cdrom)
  1119.         if not os.path.exists(target_cdrom):
  1120.             os.mkdir(target_cdrom)
  1121.         misc.execute('mount', '--bind', '/cdrom', target_cdrom)
  1122.  
  1123.         # Make apt-cdrom and apt not unmount/mount CD-ROMs.
  1124.         # This file will be left in place until the end of the install.
  1125.         apt_conf_nmc = open(os.path.join(
  1126.             self.target, 'etc/apt/apt.conf.d/00NoMountCDROM'), 'w')
  1127.         print >>apt_conf_nmc, textwrap.dedent("""\
  1128.             APT::CDROM::NoMount "true";
  1129.             Acquire::cdrom {
  1130.               mount "/cdrom";
  1131.               "/cdrom/" {
  1132.                 Mount  "true";
  1133.                 UMount "true";
  1134.               };
  1135.             }""")
  1136.         apt_conf_nmc.close()
  1137.  
  1138.         # This will be reindexed after installation based on the full
  1139.         # installed sources.list.
  1140.         try:
  1141.             shutil.rmtree(
  1142.                 os.path.join(self.target, 'var/lib/apt-xapian-index'),
  1143.                 ignore_errors=True)
  1144.         except OSError:
  1145.             pass
  1146.  
  1147.         dbfilter = apt_setup.AptSetup(None, self.db)
  1148.         ret = dbfilter.run_command(auto_process=True)
  1149.         if ret != 0:
  1150.             raise InstallStepError("AptSetup failed with code %d" % ret)
  1151.  
  1152.  
  1153.     def get_cache_pkg(self, cache, pkg):
  1154.         # work around broken has_key in python-apt 0.6.16
  1155.         try:
  1156.             return cache[pkg]
  1157.         except KeyError:
  1158.             return None
  1159.  
  1160.  
  1161.     def record_installed(self, pkgs):
  1162.         """Record which packages we've explicitly installed so that we don't
  1163.         try to remove them later."""
  1164.  
  1165.         record_file = "/var/lib/ubiquity/apt-installed"
  1166.         if not os.path.exists(os.path.dirname(record_file)):
  1167.             os.makedirs(os.path.dirname(record_file))
  1168.         record = open(record_file, "a")
  1169.  
  1170.         for pkg in pkgs:
  1171.             print >>record, pkg
  1172.  
  1173.         record.close()
  1174.  
  1175.  
  1176.     def query_recorded_installed(self):
  1177.         apt_installed = set()
  1178.         if os.path.exists("/var/lib/ubiquity/apt-installed"):
  1179.             record_file = open("/var/lib/ubiquity/apt-installed")
  1180.             for line in record_file:
  1181.                 apt_installed.add(line.strip())
  1182.             record_file.close()
  1183.         return apt_installed
  1184.  
  1185.  
  1186.     def mark_install(self, cache, pkg):
  1187.         cachedpkg = self.get_cache_pkg(cache, pkg)
  1188.         if cachedpkg is not None and not cachedpkg.isInstalled:
  1189.             apt_error = False
  1190.             try:
  1191.                 cachedpkg.markInstall()
  1192.             except SystemError:
  1193.                 apt_error = True
  1194.             if cache._depcache.BrokenCount > 0 or apt_error:
  1195.                 brokenpkgs = self.broken_packages(cache)
  1196.                 while brokenpkgs:
  1197.                     for brokenpkg in brokenpkgs:
  1198.                         self.get_cache_pkg(cache, brokenpkg).markKeep()
  1199.                     new_brokenpkgs = self.broken_packages(cache)
  1200.                     if brokenpkgs == new_brokenpkgs:
  1201.                         break # we can do nothing more
  1202.                     brokenpkgs = new_brokenpkgs
  1203.                 assert cache._depcache.BrokenCount == 0
  1204.  
  1205.  
  1206.     def select_language_packs(self):
  1207.         try:
  1208.             keep_packages = self.db.get('ubiquity/keep-installed')
  1209.             keep_packages = keep_packages.replace(',', '').split()
  1210.             syslog.syslog('keeping packages due to preseeding: %s' %
  1211.                           ' '.join(keep_packages))
  1212.             self.record_installed(keep_packages)
  1213.         except debconf.DebconfError:
  1214.             pass
  1215.  
  1216.         langpacks = []
  1217.         try:
  1218.             langpack_db = self.db.get('pkgsel/language-packs')
  1219.             langpacks = langpack_db.replace(',', '').split()
  1220.         except debconf.DebconfError:
  1221.             pass
  1222.         if not langpacks:
  1223.             try:
  1224.                 langpack_db = self.db.get('localechooser/supported-locales')
  1225.                 langpack_set = set()
  1226.                 for locale in langpack_db.replace(',', '').split():
  1227.                     langpack_set.add(locale.split('_')[0])
  1228.                 langpacks = sorted(langpack_set)
  1229.             except debconf.DebconfError:
  1230.                 pass
  1231.         if not langpacks:
  1232.             langpack_db = self.db.get('debian-installer/locale')
  1233.             langpacks = [langpack_db.split('_')[0]]
  1234.         syslog.syslog('keeping language packs for: %s' % ' '.join(langpacks))
  1235.  
  1236.         try:
  1237.             lppatterns = self.db.get('pkgsel/language-pack-patterns').split()
  1238.         except debconf.DebconfError:
  1239.             return
  1240.  
  1241.         to_install = []
  1242.         for lp in langpacks:
  1243.             # Basic language packs, required to get localisation working at
  1244.             # all. We install these almost unconditionally; if you want to
  1245.             # get rid of even these, you can preseed pkgsel/language-packs
  1246.             # to the empty string.
  1247.             to_install.append('language-pack-%s' % lp)
  1248.             # Other language packs, typically selected by preseeding.
  1249.             for pattern in lppatterns:
  1250.                 to_install.append(pattern.replace('$LL', lp))
  1251.             # More extensive language support packages.
  1252.             to_install.append('language-support-%s' % lp)
  1253.  
  1254.         # Filter the list of language packs to include only language packs
  1255.         # that exist in the live filesystem's apt cache, so that we can tell
  1256.         # the difference between "no such language pack" and "language pack
  1257.         # not retrievable given apt configuration in /target" later on.
  1258.         cache = Cache()
  1259.         to_install = [lp for lp in to_install
  1260.                          if self.get_cache_pkg(cache, lp) is not None]
  1261.         del cache
  1262.  
  1263.         self.record_installed(to_install)
  1264.         self.langpacks = to_install
  1265.  
  1266.     def install_language_packs(self):
  1267.         self.do_install(self.langpacks)
  1268.  
  1269.         cache = Cache()
  1270.         incomplete = False
  1271.         for pkg in self.langpacks:
  1272.             cachedpkg = self.get_cache_pkg(cache, pkg)
  1273.             if cachedpkg is None or not cachedpkg.isInstalled:
  1274.                 incomplete = True
  1275.                 break
  1276.         if incomplete:
  1277.             language_support_dir = \
  1278.                 os.path.join(self.target, 'usr/share/language-support')
  1279.             update_notifier_dir = \
  1280.                 os.path.join(self.target, 'var/lib/update-notifier/user.d')
  1281.             for note in ('incomplete-language-support-gnome.note',
  1282.                          'incomplete-language-support-qt.note'):
  1283.                 notepath = os.path.join(language_support_dir, note)
  1284.                 if os.path.exists(notepath):
  1285.                     if not os.path.exists(update_notifier_dir):
  1286.                         os.makedirs(update_notifier_dir)
  1287.                     shutil.copy(notepath,
  1288.                                 os.path.join(update_notifier_dir, note))
  1289.                     break
  1290.  
  1291.  
  1292.     def configure_timezone(self):
  1293.         """Set timezone on installed system."""
  1294.  
  1295.         dbfilter = timezone_apply.TimezoneApply(None)
  1296.         ret = dbfilter.run_command(auto_process=True)
  1297.         if ret != 0:
  1298.             raise InstallStepError("TimezoneApply failed with code %d" % ret)
  1299.  
  1300.         dbfilter = clock_setup.ClockSetup(None)
  1301.         ret = dbfilter.run_command(auto_process=True)
  1302.         if ret != 0:
  1303.             raise InstallStepError("ClockSetup failed with code %d" % ret)
  1304.  
  1305.  
  1306.     def configure_keyboard(self):
  1307.         """Set keyboard in installed system."""
  1308.  
  1309.         dbfilter = console_setup_apply.ConsoleSetupApply(None)
  1310.         ret = dbfilter.run_command(auto_process=True)
  1311.         if ret != 0:
  1312.             raise InstallStepError(
  1313.                 "ConsoleSetupApply failed with code %d" % ret)
  1314.  
  1315.  
  1316.     def configure_user(self):
  1317.         """create the user selected along the installation process
  1318.         into the installed system. Default user from live system is
  1319.         deleted and skel for this new user is copied to $HOME."""
  1320.  
  1321.         dbfilter = usersetup_apply.UserSetupApply(None)
  1322.         ret = dbfilter.run_command(auto_process=True)
  1323.         if ret != 0:
  1324.             raise InstallStepError("UserSetupApply failed with code %d" % ret)
  1325.  
  1326.     def configure_ma(self):
  1327.         """import documents, settings, and users from previous operating
  1328.         systems."""
  1329.  
  1330.         if 'UBIQUITY_MIGRATION_ASSISTANT' in os.environ:
  1331.             dbfilter = migrationassistant_apply.MigrationAssistantApply(None)
  1332.             ret = dbfilter.run_command(auto_process=True)
  1333.             if ret != 0:
  1334.                 raise InstallStepError("MigrationAssistantApply failed with code %d" % ret)
  1335.  
  1336.  
  1337.     def get_resume_partition(self):
  1338.         biggest_size = 0
  1339.         biggest_partition = None
  1340.         swaps = open('/proc/swaps')
  1341.         for line in swaps:
  1342.             words = line.split()
  1343.             if words[1] != 'partition':
  1344.                 continue
  1345.             if not os.path.exists(words[0]):
  1346.                 continue
  1347.             if words[0].startswith('/dev/ramzswap'):
  1348.                 continue
  1349.             size = int(words[2])
  1350.             if size > biggest_size:
  1351.                 biggest_size = size
  1352.                 biggest_partition = words[0]
  1353.         swaps.close()
  1354.         return biggest_partition
  1355.  
  1356.     def configure_hardware(self):
  1357.         """reconfiguring several packages which depends on the
  1358.         hardware system in which has been installed on and need some
  1359.         automatic configurations to get work."""
  1360.  
  1361.         self.chroot_setup()
  1362.         try:
  1363.             dbfilter = hw_detect.HwDetect(None, self.db)
  1364.             ret = dbfilter.run_command(auto_process=True)
  1365.             if ret != 0:
  1366.                 raise InstallStepError("HwDetect failed with code %d" % ret)
  1367.         finally:
  1368.             self.chroot_cleanup()
  1369.  
  1370.         self.db.progress('INFO', 'ubiquity/install/hardware')
  1371.  
  1372.         misc.execute('/usr/lib/ubiquity/debian-installer-utils'
  1373.                      '/register-module.post-base-installer')
  1374.  
  1375.         resume = self.get_resume_partition()
  1376.         if resume is not None:
  1377.             resume_uuid = None
  1378.             try:
  1379.                 resume_uuid = subprocess.Popen(
  1380.                     ['vol_id', '-u', resume],
  1381.                     stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  1382.             except OSError:
  1383.                 pass
  1384.             if resume_uuid:
  1385.                 resume = "UUID=%s" % resume_uuid
  1386.             if os.path.exists(os.path.join(self.target,
  1387.                                            'etc/initramfs-tools/conf.d')):
  1388.                 configdir = os.path.join(self.target,
  1389.                                          'etc/initramfs-tools/conf.d')
  1390.             elif os.path.exists(os.path.join(self.target,
  1391.                                              'etc/mkinitramfs/conf.d')):
  1392.                 configdir = os.path.join(self.target,
  1393.                                          'etc/mkinitramfs/conf.d')
  1394.             else:
  1395.                 configdir = None
  1396.             if configdir is not None:
  1397.                 configfile = open(os.path.join(configdir, 'resume'), 'w')
  1398.                 print >>configfile, "RESUME=%s" % resume
  1399.                 configfile.close()
  1400.  
  1401.         try:
  1402.             os.unlink('/target/etc/usplash.conf')
  1403.         except OSError:
  1404.             pass
  1405.         try:
  1406.             modes = self.db.get('xserver-xorg/config/display/modes')
  1407.             self.set_debconf('xserver-xorg/config/display/modes', modes)
  1408.         except debconf.DebconfError:
  1409.             pass
  1410.  
  1411.         try:
  1412.             os.unlink('/target/etc/popularity-contest.conf')
  1413.         except OSError:
  1414.             pass
  1415.         try:
  1416.             participate = self.db.get('popularity-contest/participate')
  1417.             self.set_debconf('popularity-contest/participate', participate)
  1418.         except debconf.DebconfError:
  1419.             pass
  1420.  
  1421.         try:
  1422.             os.unlink('/target/etc/papersize')
  1423.         except OSError:
  1424.             pass
  1425.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  1426.                          'ucf', '--purge', '/etc/papersize'],
  1427.                         preexec_fn=debconf_disconnect, close_fds=True)
  1428.         try:
  1429.             self.set_debconf('libpaper/defaultpaper', '')
  1430.         except debconf.DebconfError:
  1431.             pass
  1432.  
  1433.         try:
  1434.             os.unlink('/target/etc/ssl/certs/ssl-cert-snakeoil.pem')
  1435.         except OSError:
  1436.             pass
  1437.         try:
  1438.             os.unlink('/target/etc/ssl/private/ssl-cert-snakeoil.key')
  1439.         except OSError:
  1440.             pass
  1441.  
  1442.         self.chroot_setup(x11=True)
  1443.         self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1444.                    '--quiet', '--add', '/usr/sbin/update-initramfs')
  1445.         try:
  1446.             os.symlink('/bin/true', '/target/usr/sbin/update-initramfs')
  1447.         except OSError:
  1448.             pass
  1449.  
  1450.         packages = ['linux-image-' + self.kernel_version,
  1451.                     'linux-restricted-modules-' + self.kernel_version,
  1452.                     'usplash',
  1453.                     'splashy',
  1454.                     'popularity-contest',
  1455.                     'libpaper1',
  1456.                     'ssl-cert']
  1457.  
  1458.         try:
  1459.             for package in packages:
  1460.                 self.reconfigure(package)
  1461.         finally:
  1462.             try:
  1463.                 os.unlink('/target/usr/sbin/update-initramfs')
  1464.             except OSError:
  1465.                 pass
  1466.             self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1467.                        '--quiet', '--remove', '/usr/sbin/update-initramfs')
  1468.             self.chrex('update-initramfs', '-c', '-k', os.uname()[2])
  1469.             self.chroot_cleanup(x11=True)
  1470.  
  1471.         # Fix up kernel symlinks now that the initrd exists. Depending on
  1472.         # the architecture, these may be in / or in /boot.
  1473.         bootdir = os.path.join(self.target, 'boot')
  1474.         if self.db.get('base-installer/kernel/linux/link_in_boot') == 'true':
  1475.             linkdir = bootdir
  1476.             linkprefix = ''
  1477.         else:
  1478.             linkdir = self.target
  1479.             linkprefix = 'boot'
  1480.  
  1481.         # Remove old symlinks. We'll set them up from scratch.
  1482.         re_symlink = re.compile('vmlinu[xz]|initrd.img$')
  1483.         for entry in os.listdir(linkdir):
  1484.             if re_symlink.match(entry) is not None:
  1485.                 filename = os.path.join(linkdir, entry)
  1486.                 if os.path.islink(filename):
  1487.                     os.unlink(filename)
  1488.         if linkdir != self.target:
  1489.             # Remove symlinks in /target too, which may have been created on
  1490.             # the live filesystem. This isn't necessary, but it may help
  1491.             # avoid confusion.
  1492.             for entry in os.listdir(self.target):
  1493.                 if re_symlink.match(entry) is not None:
  1494.                     filename = os.path.join(self.target, entry)
  1495.                     if os.path.islink(filename):
  1496.                         os.unlink(filename)
  1497.  
  1498.         # Create symlinks. Prefer our current kernel version if possible,
  1499.         # but if not (perhaps due to a customised live filesystem image),
  1500.         # it's better to create some symlinks than none at all.
  1501.         re_image = re.compile('(vmlinu[xz]|initrd.img)-')
  1502.         for entry in os.listdir(bootdir):
  1503.             match = re_image.match(entry)
  1504.             if match is not None:
  1505.                 imagetype = match.group(1)
  1506.                 linksrc = os.path.join(linkprefix, entry)
  1507.                 linkdst = os.path.join(linkdir, imagetype)
  1508.                 if os.path.exists(linkdst):
  1509.                     if entry.endswith('-' + self.kernel_version):
  1510.                         os.unlink(linkdst)
  1511.                     else:
  1512.                         continue
  1513.                 os.symlink(linksrc, linkdst)
  1514.  
  1515.  
  1516.     def get_all_interfaces(self):
  1517.         """Get all non-local network interfaces."""
  1518.         ifs = []
  1519.         ifs_file = open('/proc/net/dev')
  1520.         # eat header
  1521.         ifs_file.readline()
  1522.         ifs_file.readline()
  1523.  
  1524.         for line in ifs_file:
  1525.             name = re.match('(.*?(?::\d+)?):', line.strip()).group(1)
  1526.             if name == 'lo':
  1527.                 continue
  1528.             ifs.append(name)
  1529.  
  1530.         ifs_file.close()
  1531.         return ifs
  1532.  
  1533.  
  1534.     def configure_network(self):
  1535.         """Automatically configure the network.
  1536.  
  1537.         At present, the only thing the user gets to tweak in the UI is the
  1538.         hostname. Some other things will be copied from the live filesystem,
  1539.         so changes made there will be reflected in the installed system.
  1540.  
  1541.         Unfortunately, at present we have to duplicate a fair bit of netcfg
  1542.         here, because it's hard to drive netcfg in a way that won't try to
  1543.         bring interfaces up and down."""
  1544.  
  1545.         # TODO cjwatson 2006-03-30: just call netcfg instead of doing all
  1546.         # this; requires a netcfg binary that doesn't bring interfaces up
  1547.         # and down
  1548.  
  1549.         for path in ('/etc/network/interfaces', '/etc/resolv.conf'):
  1550.             if os.path.exists(path):
  1551.                 shutil.copy2(path, os.path.join(self.target, path[1:]))
  1552.  
  1553.         try:
  1554.             hostname = self.db.get('netcfg/get_hostname')
  1555.         except debconf.DebconfError:
  1556.             hostname = ''
  1557.         try:
  1558.             domain = self.db.get('netcfg/get_domain')
  1559.         except debconf.DebconfError:
  1560.             domain = ''
  1561.         if hostname == '':
  1562.             hostname = 'ubuntu'
  1563.  
  1564.         fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
  1565.         print >>fp, hostname
  1566.         fp.close()
  1567.  
  1568.         hosts = open(os.path.join(self.target, 'etc/hosts'), 'w')
  1569.         print >>hosts, "127.0.0.1\tlocalhost"
  1570.         if domain:
  1571.             print >>hosts, "127.0.1.1\t%s.%s\t%s" % (hostname, domain,
  1572.                                                      hostname)
  1573.         else:
  1574.             print >>hosts, "127.0.1.1\t%s" % hostname
  1575.         print >>hosts, textwrap.dedent("""\
  1576.  
  1577.             # The following lines are desirable for IPv6 capable hosts
  1578.             ::1     localhost ip6-localhost ip6-loopback
  1579.             fe00::0 ip6-localnet
  1580.             ff00::0 ip6-mcastprefix
  1581.             ff02::1 ip6-allnodes
  1582.             ff02::2 ip6-allrouters
  1583.             ff02::3 ip6-allhosts""")
  1584.         hosts.close()
  1585.  
  1586.         persistent_net = '/etc/udev/rules.d/70-persistent-net.rules'
  1587.         if os.path.exists(persistent_net):
  1588.             shutil.copy2(persistent_net,
  1589.                          os.path.join(self.target, persistent_net[1:]))
  1590.         else:
  1591.             # TODO cjwatson 2006-03-30: from <bits/ioctls.h>; ugh, but no
  1592.             # binding available
  1593.             SIOCGIFHWADDR = 0x8927
  1594.             # <net/if_arp.h>
  1595.             ARPHRD_ETHER = 1
  1596.  
  1597.             if_names = {}
  1598.             sock = socket.socket(socket.SOCK_DGRAM)
  1599.             interfaces = self.get_all_interfaces()
  1600.             for i in range(len(interfaces)):
  1601.                 if_names[interfaces[i]] = struct.unpack('H6s',
  1602.                     fcntl.ioctl(sock.fileno(), SIOCGIFHWADDR,
  1603.                                 struct.pack('256s', interfaces[i]))[16:24])
  1604.             sock.close()
  1605.  
  1606.             iftab = open(os.path.join(self.target, 'etc/iftab'), 'w')
  1607.  
  1608.             print >>iftab, textwrap.dedent("""\
  1609.                 # This file assigns persistent names to network interfaces.
  1610.                 # See iftab(5) for syntax.
  1611.                 """)
  1612.  
  1613.             for i in range(len(interfaces)):
  1614.                 dup = False
  1615.                 with_arp = False
  1616.  
  1617.                 if_name = if_names[interfaces[i]]
  1618.                 if if_name is None or if_name[0] != ARPHRD_ETHER:
  1619.                     continue
  1620.  
  1621.                 for j in range(len(interfaces)):
  1622.                     if i == j or if_names[interfaces[j]] is None:
  1623.                         continue
  1624.                     if if_name[1] != if_names[interfaces[j]][1]:
  1625.                         continue
  1626.  
  1627.                     if if_names[interfaces[j]][0] == ARPHRD_ETHER:
  1628.                         dup = True
  1629.  
  1630.                 if dup:
  1631.                     continue
  1632.  
  1633.                 line = (interfaces[i] + " mac " +
  1634.                         ':'.join(['%02x' % ord(if_name[1][c])
  1635.                                   for c in range(6)]))
  1636.                 line += " arp %d" % if_name[0]
  1637.                 print >>iftab, line
  1638.  
  1639.             iftab.close()
  1640.  
  1641.  
  1642.     def configure_bootloader(self):
  1643.         """configuring and installing boot loader into installed
  1644.         hardware system."""
  1645.         install_bootloader = self.db.get('ubiquity/install_bootloader')
  1646.         if install_bootloader == "true":
  1647.             misc.execute('mount', '--bind', '/proc', self.target + '/proc')
  1648.             misc.execute('mount', '--bind', '/dev', self.target + '/dev')
  1649.  
  1650.             arch, subarch = self.archdetect()
  1651.  
  1652.             try:
  1653.                 if arch in ('amd64', 'i386', 'lpia'):
  1654.                     from ubiquity.components import grubinstaller
  1655.                     dbfilter = grubinstaller.GrubInstaller(None)
  1656.                     ret = dbfilter.run_command(auto_process=True)
  1657.                     if ret != 0:
  1658.                         raise InstallStepError(
  1659.                             "GrubInstaller failed with code %d" % ret)
  1660.                 elif (arch == 'armel' and
  1661.                       subarch in ('imx51', 'iop32x', 'ixp4xx', 'orion5x')):
  1662.                     from ubiquity.components import flash_kernel
  1663.                     dbfilter = flash_kernel.FlashKernel(None)
  1664.                     ret = dbfilter.run_command(auto_process=True)
  1665.                     if ret != 0:
  1666.                         raise InstallStepError(
  1667.                             "FlashKernel failed with code %d" % ret)
  1668.                 elif arch == 'powerpc' and subarch == 'ps3':
  1669.                     from ubiquity.components import kbootinstaller
  1670.                     dbfilter = kbootinstaller.KbootInstaller(None)
  1671.                     ret = dbfilter.run_command(auto_process=True)
  1672.                     if ret != 0:
  1673.                         raise InstallStepError(
  1674.                             "KbootInstaller failed with code %d" % ret)
  1675.                 elif arch == 'powerpc':
  1676.                     from ubiquity.components import yabootinstaller
  1677.                     dbfilter = yabootinstaller.YabootInstaller(None)
  1678.                     ret = dbfilter.run_command(auto_process=True)
  1679.                     if ret != 0:
  1680.                         raise InstallStepError(
  1681.                             "YabootInstaller failed with code %d" % ret)
  1682.                 else:
  1683.                     raise InstallStepError("No bootloader installer found")
  1684.             except ImportError:
  1685.                 raise InstallStepError("No bootloader installer found")
  1686.  
  1687.             misc.execute('umount', '-f', self.target + '/proc')
  1688.             misc.execute('umount', '-f', self.target + '/dev')
  1689.  
  1690.  
  1691.     def broken_packages(self, cache):
  1692.         expect_count = cache._depcache.BrokenCount
  1693.         count = 0
  1694.         brokenpkgs = set()
  1695.         for pkg in cache.keys():
  1696.             try:
  1697.                 if cache._depcache.IsInstBroken(cache._cache[pkg]):
  1698.                     brokenpkgs.add(pkg)
  1699.                     count += 1
  1700.             except KeyError:
  1701.                 # Apparently sometimes the cache goes a bit bonkers ...
  1702.                 continue
  1703.             if count >= expect_count:
  1704.                 break
  1705.         return brokenpkgs
  1706.  
  1707.     def do_install(self, to_install):
  1708.         if self.langpacks:
  1709.             self.db.progress('START', 0, 10, 'ubiquity/langpacks/title')
  1710.         else:
  1711.             self.db.progress('START', 0, 10, 'ubiquity/install/title')
  1712.         self.db.progress('INFO', 'ubiquity/install/find_installables')
  1713.  
  1714.         self.db.progress('REGION', 0, 1)
  1715.         fetchprogress = DebconfFetchProgress(
  1716.             self.db, 'ubiquity/install/title',
  1717.             'ubiquity/install/apt_indices_starting',
  1718.             'ubiquity/install/apt_indices')
  1719.         cache = Cache()
  1720.  
  1721.         if cache._depcache.BrokenCount > 0:
  1722.             syslog.syslog(
  1723.                 'not installing additional packages, since there are broken '
  1724.                 'packages: %s' % ', '.join(self.broken_packages(cache)))
  1725.             self.db.progress('STOP')
  1726.             return
  1727.  
  1728.         for pkg in to_install:
  1729.             self.mark_install(cache, pkg)
  1730.  
  1731.         self.db.progress('SET', 1)
  1732.         self.db.progress('REGION', 1, 10)
  1733.         if self.langpacks:
  1734.             fetchprogress = DebconfFetchProgress(
  1735.                 self.db, 'ubiquity/langpacks/title', None,
  1736.                 'ubiquity/langpacks/packages')
  1737.             installprogress = DebconfInstallProgress(
  1738.                 self.db, 'ubiquity/langpacks/title',
  1739.                 'ubiquity/install/apt_info')
  1740.         else:
  1741.             fetchprogress = DebconfFetchProgress(
  1742.                 self.db, 'ubiquity/install/title', None,
  1743.                 'ubiquity/install/fetch_remove')
  1744.             installprogress = DebconfInstallProgress(
  1745.                 self.db, 'ubiquity/install/title',
  1746.                 'ubiquity/install/apt_info',
  1747.                 'ubiquity/install/apt_error_install')
  1748.         self.chroot_setup()
  1749.         commit_error = None
  1750.         try:
  1751.             try:
  1752.                 if not cache.commit(fetchprogress, installprogress):
  1753.                     fetchprogress.stop()
  1754.                     installprogress.finishUpdate()
  1755.                     self.db.progress('STOP')
  1756.                     return
  1757.             except IOError, e:
  1758.                 for line in str(e).split('\n'):
  1759.                     syslog.syslog(syslog.LOG_ERR, line)
  1760.                 fetchprogress.stop()
  1761.                 installprogress.finishUpdate()
  1762.                 self.db.progress('STOP')
  1763.                 return
  1764.             except SystemError, e:
  1765.                 for line in str(e).split('\n'):
  1766.                     syslog.syslog(syslog.LOG_ERR, line)
  1767.                 commit_error = str(e)
  1768.         finally:
  1769.             self.chroot_cleanup()
  1770.         self.db.progress('SET', 10)
  1771.  
  1772.         cache.open(None)
  1773.         if commit_error or cache._depcache.BrokenCount > 0:
  1774.             if commit_error is None:
  1775.                 commit_error = ''
  1776.             brokenpkgs = self.broken_packages(cache)
  1777.             syslog.syslog('broken packages after installation: '
  1778.                           '%s' % ', '.join(brokenpkgs))
  1779.             self.db.subst('ubiquity/install/broken_install', 'ERROR',
  1780.                           commit_error)
  1781.             self.db.subst('ubiquity/install/broken_install', 'PACKAGES',
  1782.                           ', '.join(brokenpkgs))
  1783.             self.db.input('critical', 'ubiquity/install/broken_install')
  1784.             self.db.go()
  1785.  
  1786.         self.db.progress('STOP')
  1787.  
  1788.  
  1789.     def expand_dependencies_simple(self, cache, keep, to_remove,
  1790.                                    recommends=True):
  1791.         """Return the list of packages in to_remove that clearly cannot be
  1792.         removed if we want to keep the set of packages in keep. Except in
  1793.         the case of Recommends, this is not required for correctness (we
  1794.         could just let apt figure it out), but it allows us to ask apt fewer
  1795.         separate questions, and so is faster."""
  1796.  
  1797.         keys = ['Pre-Depends', 'Depends']
  1798.         if recommends:
  1799.             keys.append('Recommends')
  1800.  
  1801.         to_scan = set(keep)
  1802.         to_scan_next = set()
  1803.         expanded = set(keep)
  1804.         while to_scan:
  1805.             for pkg in to_scan:
  1806.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1807.                 if cachedpkg is None:
  1808.                     continue
  1809.                 ver = cachedpkg._pkg.CurrentVer
  1810.                 if ver is None:
  1811.                     continue
  1812.                 for key in keys:
  1813.                     if key in ver.DependsList:
  1814.                         for dep_or in ver.DependsList[key]:
  1815.                             # Keep the first element of a disjunction that's
  1816.                             # installed; this mirrors what 'apt-get install'
  1817.                             # would do if you were installing the package
  1818.                             # from scratch. This doesn't handle versioned
  1819.                             # dependencies, but that's largely OK since apt
  1820.                             # will spot those later; the only case I can
  1821.                             # think of where this might have trouble is
  1822.                             # "Recommends: foo (>= 2) | bar".
  1823.                             for dep in dep_or:
  1824.                                 depname = dep.TargetPkg.Name
  1825.                                 cacheddep = self.get_cache_pkg(cache, depname)
  1826.                                 if cacheddep is None:
  1827.                                     continue
  1828.                                 if cacheddep._pkg.CurrentVer is not None:
  1829.                                     break
  1830.                             else:
  1831.                                 continue
  1832.                             if depname in expanded or depname not in to_remove:
  1833.                                 continue
  1834.                             expanded.add(depname)
  1835.                             to_scan_next.add(depname)
  1836.             to_scan = to_scan_next
  1837.             to_scan_next = set()
  1838.  
  1839.         return expanded
  1840.  
  1841.  
  1842.     def get_remove_list(self, cache, to_remove, recursive=False):
  1843.         to_remove = set(to_remove)
  1844.         all_removed = set()
  1845.         while True:
  1846.             removed = set()
  1847.             for pkg in to_remove:
  1848.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1849.                 if cachedpkg is not None and cachedpkg.isInstalled:
  1850.                     apt_error = False
  1851.                     try:
  1852.                         cachedpkg.markDelete(autoFix=False, purge=True)
  1853.                     except SystemError:
  1854.                         apt_error = True
  1855.                     if apt_error:
  1856.                         cachedpkg.markKeep()
  1857.                     elif cache._depcache.BrokenCount > 0:
  1858.                         # If we're recursively removing packages, or if all
  1859.                         # of the broken packages are in the set of packages
  1860.                         # to remove anyway, then go ahead and try to remove
  1861.                         # them too.
  1862.                         brokenpkgs = self.broken_packages(cache)
  1863.                         broken_removed = set()
  1864.                         while brokenpkgs and (recursive or
  1865.                                               brokenpkgs <= to_remove):
  1866.                             broken_removed_inner = set()
  1867.                             for pkg2 in brokenpkgs:
  1868.                                 cachedpkg2 = self.get_cache_pkg(cache, pkg2)
  1869.                                 if cachedpkg2 is not None:
  1870.                                     broken_removed_inner.add(pkg2)
  1871.                                     try:
  1872.                                         cachedpkg2.markDelete(autoFix=False,
  1873.                                                               purge=True)
  1874.                                     except SystemError:
  1875.                                         apt_error = True
  1876.                                         break
  1877.                             broken_removed |= broken_removed_inner
  1878.                             if apt_error or not broken_removed_inner:
  1879.                                 break
  1880.                             brokenpkgs = self.broken_packages(cache)
  1881.                         if apt_error or cache._depcache.BrokenCount > 0:
  1882.                             # That didn't work. Revert all the removals we
  1883.                             # just tried.
  1884.                             for pkg2 in broken_removed:
  1885.                                 self.get_cache_pkg(cache, pkg2).markKeep()
  1886.                             cachedpkg.markKeep()
  1887.                         else:
  1888.                             removed.add(pkg)
  1889.                             removed |= broken_removed
  1890.                     else:
  1891.                         removed.add(pkg)
  1892.                     assert cache._depcache.BrokenCount == 0
  1893.             if not removed:
  1894.                 break
  1895.             to_remove -= removed
  1896.             all_removed |= removed
  1897.         return all_removed
  1898.  
  1899.  
  1900.     def do_remove(self, to_remove, recursive=False):
  1901.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  1902.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1903.  
  1904.         fetchprogress = DebconfFetchProgress(
  1905.             self.db, 'ubiquity/install/title',
  1906.             'ubiquity/install/apt_indices_starting',
  1907.             'ubiquity/install/apt_indices')
  1908.         cache = Cache()
  1909.  
  1910.         if cache._depcache.BrokenCount > 0:
  1911.             syslog.syslog(
  1912.                 'not processing removals, since there are broken packages: '
  1913.                 '%s' % ', '.join(self.broken_packages(cache)))
  1914.             self.db.progress('STOP')
  1915.             return
  1916.  
  1917.         self.get_remove_list(cache, to_remove, recursive)
  1918.  
  1919.         self.db.progress('SET', 1)
  1920.         self.db.progress('REGION', 1, 5)
  1921.         fetchprogress = DebconfFetchProgress(
  1922.             self.db, 'ubiquity/install/title', None,
  1923.             'ubiquity/install/fetch_remove')
  1924.         installprogress = DebconfInstallProgress(
  1925.             self.db, 'ubiquity/install/title', 'ubiquity/install/apt_info',
  1926.             'ubiquity/install/apt_error_remove')
  1927.         self.chroot_setup()
  1928.         commit_error = None
  1929.         try:
  1930.             try:
  1931.                 if not cache.commit(fetchprogress, installprogress):
  1932.                     fetchprogress.stop()
  1933.                     installprogress.finishUpdate()
  1934.                     self.db.progress('STOP')
  1935.                     return
  1936.             except SystemError, e:
  1937.                 for line in str(e).split('\n'):
  1938.                     syslog.syslog(syslog.LOG_ERR, line)
  1939.                 commit_error = str(e)
  1940.         finally:
  1941.             self.chroot_cleanup()
  1942.         self.db.progress('SET', 5)
  1943.  
  1944.         cache.open(None)
  1945.         if commit_error or cache._depcache.BrokenCount > 0:
  1946.             if commit_error is None:
  1947.                 commit_error = ''
  1948.             brokenpkgs = self.broken_packages(cache)
  1949.             syslog.syslog('broken packages after removal: '
  1950.                           '%s' % ', '.join(brokenpkgs))
  1951.             self.db.subst('ubiquity/install/broken_remove', 'ERROR',
  1952.                           commit_error)
  1953.             self.db.subst('ubiquity/install/broken_remove', 'PACKAGES',
  1954.                           ', '.join(brokenpkgs))
  1955.             self.db.input('critical', 'ubiquity/install/broken_remove')
  1956.             self.db.go()
  1957.  
  1958.         self.db.progress('STOP')
  1959.  
  1960.  
  1961.     def remove_unusable_kernels(self):
  1962.         """Remove unusable kernels; keeping them may cause us to be unable
  1963.         to boot."""
  1964.  
  1965.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  1966.  
  1967.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1968.  
  1969.         # Check for kernel packages to remove.
  1970.         dbfilter = check_kernels.CheckKernels(None)
  1971.         dbfilter.run_command(auto_process=True)
  1972.  
  1973.         remove_kernels = set()
  1974.         if os.path.exists("/var/lib/ubiquity/remove-kernels"):
  1975.             remove_kernels_file = open("/var/lib/ubiquity/remove-kernels")
  1976.             for line in remove_kernels_file:
  1977.                 remove_kernels.add(line.strip())
  1978.             remove_kernels_file.close()
  1979.  
  1980.         if len(remove_kernels) == 0:
  1981.             self.db.progress('STOP')
  1982.             return
  1983.  
  1984.         self.db.progress('SET', 1)
  1985.         self.db.progress('REGION', 1, 5)
  1986.         try:
  1987.             self.do_remove(remove_kernels, recursive=True)
  1988.         except:
  1989.             self.db.progress('STOP')
  1990.             raise
  1991.         self.db.progress('SET', 5)
  1992.         self.db.progress('STOP')
  1993.  
  1994.  
  1995.     def install_extras(self):
  1996.         """Try to install additional packages requested by installer
  1997.         components."""
  1998.  
  1999.         # We only ever install these packages from the CD.
  2000.         sources_list = os.path.join(self.target, 'etc/apt/sources.list')
  2001.         os.rename(sources_list, "%s.apt-setup" % sources_list)
  2002.         old_sources = open("%s.apt-setup" % sources_list)
  2003.         new_sources = open(sources_list, 'w')
  2004.         found_cdrom = False
  2005.         for line in old_sources:
  2006.             if 'cdrom:' in line:
  2007.                 print >>new_sources, line,
  2008.                 found_cdrom = True
  2009.         new_sources.close()
  2010.         old_sources.close()
  2011.         if not found_cdrom:
  2012.             os.rename("%s.apt-setup" % sources_list, sources_list)
  2013.  
  2014.         self.do_install(self.query_recorded_installed())
  2015.  
  2016.         if found_cdrom:
  2017.             os.rename("%s.apt-setup" % sources_list, sources_list)
  2018.  
  2019.         # TODO cjwatson 2007-08-09: python reimplementation of
  2020.         # oem-config/finish-install.d/07oem-config-user. This really needs
  2021.         # to die in a great big chemical fire and call the same shell script
  2022.         # instead.
  2023.         try:
  2024.             if self.db.get('oem-config/enable') == 'true':
  2025.                 if os.path.isdir(os.path.join(self.target, 'home/oem')):
  2026.                     open(os.path.join(self.target, 'home/oem/.hwdb'),
  2027.                          'w').close()
  2028.  
  2029.                     for desktop_file in (
  2030.                         'usr/share/applications/oem-config-prepare-gtk.desktop',
  2031.                         'usr/share/applications/kde/oem-config-prepare-kde.desktop'):
  2032.                         if os.path.exists(os.path.join(self.target,
  2033.                                                        desktop_file)):
  2034.                             desktop_base = os.path.basename(desktop_file)
  2035.                             self.chrex('install', '-d',
  2036.                                        '-o', 'oem', '-g', 'oem',
  2037.                                        '/home/oem/Desktop')
  2038.                             self.chrex('install', '-o', 'oem', '-g', 'oem',
  2039.                                        '/%s' % desktop_file,
  2040.                                        '/home/oem/Desktop/%s' % desktop_base)
  2041.                             break
  2042.  
  2043.         # Carry the locale setting over to the installed system.
  2044.         # This mimics the behavior in 01oem-config-udeb.
  2045.                 di_locale = self.db.get('debian-installer/locale')
  2046.                 if di_locale:
  2047.                     self.set_debconf('debian-installer/locale', di_locale)
  2048.         except debconf.DebconfError:
  2049.             pass
  2050.  
  2051.  
  2052.     def remove_extras(self):
  2053.         """Try to remove packages that are needed on the live CD but not on
  2054.         the installed system."""
  2055.  
  2056.         # Looking through files for packages to remove is pretty quick, so
  2057.         # don't bother with a progress bar for that.
  2058.  
  2059.         # Check for packages specific to the live CD.
  2060.         if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
  2061.             os.path.exists("/cdrom/casper/filesystem.manifest")):
  2062.             desktop_packages = set()
  2063.             manifest = open("/cdrom/casper/filesystem.manifest-desktop")
  2064.             for line in manifest:
  2065.                 if line.strip() != '' and not line.startswith('#'):
  2066.                     desktop_packages.add(line.split()[0])
  2067.             manifest.close()
  2068.             live_packages = set()
  2069.             manifest = open("/cdrom/casper/filesystem.manifest")
  2070.             for line in manifest:
  2071.                 if line.strip() != '' and not line.startswith('#'):
  2072.                     live_packages.add(line.split()[0])
  2073.             manifest.close()
  2074.             difference = live_packages - desktop_packages
  2075.         else:
  2076.             difference = set()
  2077.  
  2078.         # Keep packages we explicitly installed.
  2079.         keep = self.query_recorded_installed()
  2080.  
  2081.         arch, subarch = self.archdetect()
  2082.  
  2083.         if arch in ('amd64', 'i386', 'lpia'):
  2084.             if 'grub' not in keep:
  2085.                 difference.add('grub')
  2086.             if 'grub-pc' not in keep:
  2087.                 difference.add('grub-pc')
  2088.             if 'lilo' not in keep:
  2089.                 difference.add('lilo')
  2090.  
  2091.         cache = Cache()
  2092.         difference -= self.expand_dependencies_simple(cache, keep, difference)
  2093.         del cache
  2094.  
  2095.         if len(difference) == 0:
  2096.             return
  2097.  
  2098.         use_restricted = True
  2099.         try:
  2100.             if self.db.get('apt-setup/restricted') == 'false':
  2101.                 use_restricted = False
  2102.         except debconf.DebconfError:
  2103.             pass
  2104.         if not use_restricted:
  2105.             cache = self.restricted_cache
  2106.             for pkg in cache.keys():
  2107.                 if (cache[pkg].isInstalled and
  2108.                     cache[pkg].section.startswith('restricted/')):
  2109.                     difference.add(pkg)
  2110.             del cache
  2111.  
  2112.         # Don't worry about failures removing packages; it will be easier
  2113.         # for the user to sort them out with a graphical package manager (or
  2114.         # whatever) after installation than it will be to try to deal with
  2115.         # them automatically here.
  2116.         self.do_remove(difference)
  2117.  
  2118.     def remove_broken_cdrom(self):
  2119.         fstab = os.path.join(self.target, 'etc/fstab')
  2120.         ret = []
  2121.         try:
  2122.             fp = open(fstab)
  2123.             for line in fp:
  2124.                 l = line.split()
  2125.                 if len(l) > 2:
  2126.                     if l[1].startswith('/cdrom') or l[1].startswith('/media/cdrom'):
  2127.                         try:
  2128.                             fstype = subprocess.Popen(
  2129.                                 ['vol_id', '--type', l[0]],
  2130.                                 stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  2131.                             if fstype != 'iso9660' and fstype != 'udf':
  2132.                                 continue
  2133.                         except OSError:
  2134.                             pass
  2135.                 ret.append(line)
  2136.             fp.close()
  2137.             fp = open(fstab, 'w')
  2138.             fp.writelines(ret)
  2139.         except Exception, e:
  2140.             syslog.syslog(syslog.LOG_ERR, 'Exception during installation:')
  2141.             syslog.syslog(syslog.LOG_ERR,
  2142.                 'Unable to process /etc/fstab: ' + str(e))
  2143.         finally:
  2144.             if fp:
  2145.                 fp.close()
  2146.             
  2147.     def cleanup(self):
  2148.         """Miscellaneous cleanup tasks."""
  2149.  
  2150.         misc.execute('umount', os.path.join(self.target, 'cdrom'))
  2151.  
  2152.         env = dict(os.environ)
  2153.         env['OVERRIDE_BASE_INSTALLABLE'] = '1'
  2154.         subprocess.call(['/usr/lib/ubiquity/apt-setup/finish-install'],
  2155.                         env=env)
  2156.  
  2157.         for apt_conf in ('00NoMountCDROM', '00IgnoreTimeConflict',
  2158.                          '00AllowUnauthenticated'):
  2159.             try:
  2160.                 os.unlink(os.path.join(
  2161.                     self.target, 'etc/apt/apt.conf.d', apt_conf))
  2162.             except:
  2163.                 pass
  2164.  
  2165.         if self.source == '/var/lib/ubiquity/source':
  2166.             self.umount_source()
  2167.  
  2168.  
  2169.     def chrex(self, *args):
  2170.         """executes commands on chroot system (provided by *args)."""
  2171.         return misc.execute('chroot', self.target, *args)
  2172.  
  2173.  
  2174.     def copy_debconf(self, package):
  2175.         """setting debconf database into installed system."""
  2176.  
  2177.         # TODO cjwatson 2006-02-25: unusable here now because we have a
  2178.         # running debconf frontend that's locked the database; fortunately
  2179.         # this isn't critical. We still need to think about how to handle
  2180.         # preseeding in general, though.
  2181.         targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
  2182.  
  2183.         misc.execute('debconf-copydb', 'configdb', 'targetdb', '-p',
  2184.                      '^%s/' % package, '--config=Name:targetdb',
  2185.                      '--config=Driver:File','--config=Filename:' + targetdb)
  2186.  
  2187.  
  2188.     def set_debconf(self, question, value):
  2189.         dccomm = subprocess.Popen(['log-output', '-t', 'ubiquity',
  2190.                                    '--pass-stdout',
  2191.                                    'chroot', self.target,
  2192.                                    'debconf-communicate',
  2193.                                    '-fnoninteractive', 'ubiquity'],
  2194.                                   stdin=subprocess.PIPE,
  2195.                                   stdout=subprocess.PIPE, close_fds=True)
  2196.         try:
  2197.             dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
  2198.             dc.set(question, value)
  2199.             dc.fset(question, 'seen', 'true')
  2200.         finally:
  2201.             dccomm.stdin.close()
  2202.             dccomm.wait()
  2203.  
  2204.  
  2205.     def reconfigure_preexec(self):
  2206.         debconf_disconnect()
  2207.         os.environ['XAUTHORITY'] = '/root/.Xauthority'
  2208.  
  2209.     def reconfigure(self, package):
  2210.         """executes a dpkg-reconfigure into installed system to each
  2211.         package which provided by args."""
  2212.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  2213.                          'dpkg-reconfigure', '-fnoninteractive', package],
  2214.                         preexec_fn=self.reconfigure_preexec, close_fds=True)
  2215.  
  2216.  
  2217. if __name__ == '__main__':
  2218.     if not os.path.exists('/var/lib/ubiquity'):
  2219.         os.makedirs('/var/lib/ubiquity')
  2220.     if os.path.exists('/var/lib/ubiquity/install.trace'):
  2221.         os.unlink('/var/lib/ubiquity/install.trace')
  2222.  
  2223.     install = Install()
  2224.     sys.excepthook = install.excepthook
  2225.     install.run()
  2226.     sys.exit(0)
  2227.  
  2228. # vim:ai:et:sts=4:tw=80:sw=4:
  2229.